#ifndef __MY_LOG_H
#define __MY_LOG_H

#include <stdio.h>
#include <iostream>
#include <string>
#include <windows.h>
#include <sstream>
#include "CriticalSectionWrapper.h"

enum Level
{
  ERR,
  WARN,
  DEBUG,
  INFO
};

class MyLog
{
    static MyLog* _self;
  protected:
    MyLog(){}
  public:
    static Level logLevel;

    static MyLog* Instance(std::string fileName)
    {
      currentFileName = fileName;
      if(!_self) _self = new MyLog();
      return _self;
    }

    void Log(Level level, std::string message)
    {
      if(level < logLevel)
        return;
      //Entering Critiacal section
      critical_section.Acquire();
      //Find file where can be written
      int temp = currentFileNumber;
      if(buffer.size() + message.size() > buffer_size)
      {
        FILE *currentFile;
        bool isWritten = false;
        //This is done, in occasion, that if program will be ran second...etc times,
        //log will find first not fully written file and will write there.
        //But it takes a little bit longer
        for(currentFileNumber; currentFileNumber <= rotationNumberFiles; currentFileNumber++)
        {
          currentFileName = basicFileName + intToString(currentFileNumber) + ".txt";
          currentFile = fopen(currentFileName.c_str(),"a+");
          if(can_write_into_file(currentFile))
          {
            fprintf(currentFile, "%s", buffer.c_str());
            fclose(currentFile);
            currentFileNumber;
            isWritten = true;
            break;
          }
          else
          {
            fclose(currentFile);
          }
        }
        //If it it wasn't written, then next file will be overwritten
        if(!isWritten)
        {
          if(temp != rotationNumberFiles)
          {
            currentFileNumber = temp + 1;
            currentFileName = basicFileName + intToString(currentFileNumber) + ".txt";
            currentFile = fopen(currentFileName.c_str(), "w+");
            fprintf(currentFile, "%s", buffer.c_str());
            fclose(currentFile);
          }
          else
          {
            currentFileNumber = 1;
            currentFileName = basicFileName + intToString(currentFileNumber) + ".txt";
            currentFile = fopen(currentFileName.c_str(), "w+");
            fprintf(currentFile, "%s", buffer.c_str());
            fclose(currentFile);
          }
        }
        buffer.clear();
      }
      switch(level){
        case Level::ERR:
          buffer.append("ERROR:"+ message +"\n");
          break;
        case Level::DEBUG:
          buffer.append("DEBUG:"+ message +"\n");
          break;
        case Level::WARN:
          buffer.append("WARN:"+ message +"\n");
          break;
        case Level::INFO:
          buffer.append("INFO:"+ message +"\n");
          break;
        default:
          buffer.append("UNKNOWNLEVEL:"+ message +"\n");
          break;
      }
      //std::cout << buffer;
      //Leaving Critical section
      critical_section.Release();
    }

    //init to initialize logger
    static void init(int rotationNumberFiles, int maxFileSize)
    {
      setRotationNumberFiles(rotationNumberFiles);
      setMaxFileSize(maxFileSize);
      currentFileName = basicFileName + intToString(currentFileNumber) + ".txt";
    }

    static void setRotationNumberFiles(int rotationNumberFiles)
    {
      MyLog::rotationNumberFiles = rotationNumberFiles;
    }
    static int getRotationNumberFiles()
    {
      return MyLog::rotationNumberFiles;
    }

    static void setMaxFileSize(int maxFileSize)
    {
      MyLog::maxFileSize = maxFileSize;
    }
    static int getMaxFileSize()
    {
      return MyLog::maxFileSize;
    }

    //if there is some info in the buffer we should flush
    //not to loose it, after the program
    static void flush()
    {
      FILE *currentFile = fopen(currentFileName.c_str(),"a+");
      fprintf(currentFile, "%s", buffer.c_str());
      fclose(currentFile);
      buffer.clear();
    }

private:
  const static int buffer_size = 1024;
  static int rotationNumberFiles;
  static int maxFileSize;
  static int currentFileNumber;
  static std::string buffer;
  static std::string currentFileName;
  const static std::string basicFileName;
  static CritSectionWrapper critical_section;

  static bool can_write_into_file(FILE* file)
  {
    long file_size = 0;
    if (file)
    {
        fseek (file, 0, SEEK_END);
        file_size = ftell(file);
    }
    if(file_size + buffer_size > maxFileSize)
      return false;
    return true;
  }

  static std::string intToString(const int& i)
  {
    std::ostringstream out;
    out << i;
    std::string temp_str = out.str();
    return temp_str;
  }

};
#endif //__MY_LOG_H
